package com.cxs.shiro.filter;

import com.alibaba.fastjson.JSON;
import com.cxs.shiro.AuthToken;
import com.cxs.vo.Result;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.SignatureException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/*
 * @Project:spring-boot-shiro-demo
 * @Author:cxs
 * @Motto:放下杂念,只为迎接明天更好的自己
 * */
public class AuthFilter extends AuthenticatingFilter {

    //创建我们自定义的AuthToken，token从请求头中获取
    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String token = httpServletRequest.getHeader("token");
        return new AuthToken(token);
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        //允许option请求通过，option 预检请求
        if (((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())) {
            return true;
        }
        return false;
    }

    //拒绝访问的请求，onAccessDenied方法先获取 token，再调用executeLogin方法
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        // 如果请求未携带token令牌，直接拒绝
        String token = httpServletRequest.getHeader("token");
        if (!StringUtils.hasLength(token)) {
            Result result = new Result();
            result.setMsg("未认证，请先登录").setCode(HttpStatus.UNAUTHORIZED.value());
            response(httpServletResponse, result);
            return false;
        }
        // 执行自定义登录逻辑
        return executeLogin(request, response);
    }

    //token失效时调用
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        Throwable cause = e.getCause();
        try {
            if (cause instanceof SignatureException) {
                Result result = new Result();
                result.setMsg("非法令牌，验证失败").setCode(HttpStatus.UNAUTHORIZED.value());
                response(httpResponse, result);
            } else if (cause instanceof ExpiredJwtException) {
                httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
                Result result = new Result();
                result.setMsg("令牌已过期,请重新登陆").setCode(HttpStatus.UNAUTHORIZED.value());
                response(httpResponse, result);
            } else if (cause instanceof JwtException) {
                Result result = new Result();
                result.setMsg("非法令牌，验证失败").setCode(HttpStatus.UNAUTHORIZED.value());
                response(httpResponse, result);
            } else {
                throw e;
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return false;
    }

    /**
     * 响应前端
     * @param response
     * @param result
     * @throws IOException
     */
    private void response(HttpServletResponse response, Result result) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Origin", response.getHeader("Origin"));
        PrintWriter writer = response.getWriter();
        writer.write(JSON.toJSONString(result));
        writer.close();
    }
}
